using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace WindowsPhoneGame1.Shapes
{
    public class Line : Shape
    {
        public PointF A { get; set; }
        public PointF B { get; set; }
        public override RectangleF Bounds { get; protected set; }

        public Line(PointF a, PointF b) : base()
        {
            A = a;
            B = b;
            SetBounds();
        }

        /// <summary>
        /// Checks if this line segment contains another segment.
        /// Returns Disjoint if the two line segments don't touch.
        /// Returns Contains if this line segment completely overlaps the other line segment.
        /// Returns Intersects if the lines cross, or if pars of the lines overlaps.
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public ContainmentType Contains(Line other)
        {
            if (Bounds.Contains(other.Bounds) == ContainmentType.Disjoint)
                return ContainmentType.Disjoint;

            float denominator =
                (other.B.Y - other.A.Y) * (B.X - A.X) -
                (other.B.X - other.A.X) * (B.Y - A.Y);

            if (denominator == 0)
            // lines are parallel
            {
                ContainmentType cA = Contains(other.A);
                ContainmentType cB = Contains(other.B);

                if(cA == ContainmentType.Disjoint && cB == ContainmentType.Disjoint)
                    return ContainmentType.Disjoint;

                if (cA != ContainmentType.Disjoint && cB != ContainmentType.Disjoint)
                    return ContainmentType.Contains;

                return ContainmentType.Intersects;
            }

            float ua, ub, numerator;

            numerator =
                (other.B.X - other.A.X) * (A.Y - other.A.Y) -
                (other.B.Y - other.A.Y) * (A.X - other.A.X);

            ua = numerator / denominator;

            if (ua < 0 || ua > 1)
                // lines don't intersect
                return ContainmentType.Disjoint;

            numerator =
                (B.X - A.X) * (A.Y - other.A.Y) -
                (B.Y - A.Y) * (A.X - other.A.X);

            ub = numerator / denominator;

            if (ub < 0 || ub > 1)
                // lines don't intersect.
                return ContainmentType.Disjoint;

            return ContainmentType.Intersects;
        }

        public override void SetBounds()
        {
            float minX, maxX, minY, maxY;

            if (A.X < B.X)
            {
                minX = A.X;
                maxX = B.X;
            }
            else
            {
                minX = B.X;
                maxX = A.X;
            }

            if (A.Y < B.Y)
            {
                minY = A.Y;
                maxY = B.Y;
            }
            else
            {
                minY = B.Y;
                maxY = A.Y;
            }

            Bounds = new RectangleF(minX, minY, maxX - minX, maxY - minY);
        }

        /// <summary>
        /// Checks if this line segment contains a point.
        /// Returns Disjoint if the point is not on the line segment.
        /// Returns Contains if the point is between the line segment's end points.
        /// Returns Intersects if the point is on one of the line segment's end points.
        /// </summary>
        /// <param name="other">The point that may be contained in this line segment.</param>
        /// <returns></returns>
        public ContainmentType Contains(PointF other)
        {
            if (A.Contains(other) == ContainmentType.Intersects ||
                B.Contains(other) == ContainmentType.Intersects)
                return ContainmentType.Intersects;

            // check if the point is in the line's bounding box.
            if (Bounds.Contains(other) == ContainmentType.Disjoint)
                return ContainmentType.Disjoint;

            // check if the point is in line with this line segment
            Vector2 v1 = new Vector2(B.X - A.X, B.Y - A.Y);
            Vector2 v2 = new Vector2(other.X - A.X, other.Y - A.Y);

            float crossProduct = v1.X * v2.Y - v1.Y * v2.X;
            if(crossProduct < 0)
                crossProduct = -crossProduct;
            
            if (crossProduct > MARGIN)
                // the point is not on the same line as this line segment
                return ContainmentType.Disjoint;

            return ContainmentType.Contains;
        }

        public override ContainmentType contains(Shape shape)
        {
            if (shape is PointF)
                return Contains((PointF)shape);
            if (shape is Line)
                return Contains((Line)shape);
            throw new ArgumentException("Does not support collision with type " + shape.GetType());
        }
    }
}
